#include "fast_build_client.h"

#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <cstdlib>
#include <cstdio>

#include <sys/types.h>
#include <sys/wait.h>

#include "structures.h"
#include "fast_build_helper.h"
#include "client_server.pb.h"
#include "message.pb.h"

using namespace std;

FBuildClient::FBuildClient(char* host, unsigned short port) :
    		Server(host, port) {
	cout << "Starting Fbuild client " << endl;
}

FBuildClient::FBuildClient(char* host, unsigned short port,
		MakefileDeps makefile_deps) : Server(host, port),
		makefile_deps_(makefile_deps) {
	cout << "Starting Fbuild client " << endl;
}

void read_config(const char *file, char *ip, char *port)
{
	ifstream config (file, ios::in);
	int size = 30;
	char *ind;

	config.open(file);

	config.getline(ip, size);

	config.getline(port, size);

	if (!strlen(ip) || !strlen(port))
		cerr << "Unable to retrieve Master's IP and port from master.config";

	ind = rindex(ip, '\n');
	if (ind)
		ind = '\0';

	ind = rindex(port, '\n');
	if (ind)
		ind = '\0';
}

void FBuildClient::HandleRequest(Socket* socket) {
	size_t length;
	int rv = socket->Receive(&length, sizeof(length));

  char* command_cstr = new char[length+1];
  unsigned int bytes_recv = 0;
  while (bytes_recv < length) {
    rv = socket->Receive(command_cstr + bytes_recv, length);
    bytes_recv += rv;
  }
  command_cstr[length] = '\0';
  string tmp(command_cstr);
  Command command_arg;
  command_arg.ParseFromString(tmp);

  cout << "Command " << endl;
  for (int i = 0; i < command_arg.command_size(); i++) {
    cout << " " << command_arg.command(i);
  }
  cout << endl;

  // TODO: Remove this
  socket->Send(&length, sizeof(length));
  return;

  // TODO : Once we get the command, carry out the rest of the operation.
  char *masterIP = new char[20];
  char *masterPort = new char[10];
  string temp("./master.config");
  const char *file = temp.c_str();

  read_config(file, masterIP, masterPort);


  // TODO (Nipun) : The request handling part should take the command and send
  // it to the server to fetch the ip:port of the slave and spawn a new process
  // running distcc


  //For now, we don't have filename (thus filesize) from the command
  //so, we use a filesize of 0.

  Socket *master = new Socket();

  if (!master->Create()) {
    cerr << " Error creating " << endl;
    exit(1);
  }
  string ip;
  if (master->Connect("172.22.28.75", (short unsigned int) 9999)) {

    uint32_t reqType = client_request;

    master->Send((void *) &reqType, sizeof(reqType));

    ClientRequest request;
    request.set_filesize(0);	/*File size 0 for now*/

    string request_cstr;
    request.SerializeToString(&request_cstr);

    size_t length = request_cstr.size();
    master->Send(&length, sizeof(length));

    char temp[length + 1];
    strncpy(temp, request_cstr.c_str(), length);
    temp[length] = '\0';

    master->Send(temp, length);

    //Now wait for response from server
    ServerResponse response;

    master->Receive(&length, sizeof(length));
    char response_cstr[length + 1];
    //Wait for server's reply
    master->Receive(response_cstr, length);
    response_cstr[length] = '\0';

    if (response.ip_size() > 0) {
    	ip.assign(response.ip(0));
    } else {
    	ip = "";
    }

  } else {
    cerr << "Unable to connect to master, defaulting to localhost" << endl;
    ip = "";
  }
  pid_t pid;

  char ** env = new char *[2];
  env[0] = new char[50];
  string s;

  if (ip.compare("") == 0) {
    s = string("DISTCC_HOSTS=\"localhost");
  } else {
    s = string("DISTCC_HOSTS=\"localhost @") + ip + "\"";
  }
  strncpy(env[0], s.c_str(), 50);

  env[1] = (char *)0;

  char **command = new char*[3];
  command[0] = new char[20];
  strncpy(command[0], "distcc", 20);
  command[1] = new char[10000];
  strncpy(command[1], command_cstr, 10000);

  command[2] = (char *) 0;

  //We know the destination, spawn distcc
  if ((pid = fork()) == 0) {

    if (execve(command[0], (char * const*) command, (char * const*) env) == -1) {
      perror("");
      _exit(EXIT_FAILURE);
    }

    _exit(EXIT_SUCCESS);
  } else {
    //in parent, wait for distcc to finish
    int status;

    wait(&status);

    if (WIFEXITED(status)) {
      cerr << "distcc exited normally" << endl;
    } else {
      cerr << "distcc exited abnormally" << endl;
    }


    socket->Send(&length, sizeof(length));

    delete[] command_cstr;
    delete [] command;
    delete [] env;
    delete masterIP;
    delete masterPort;

  }
  // Get command string from local script
  // contact server with file size (in dep map), get back an IP address
  // Fork distcc
  //
}
